﻿using Furion.CMS.Application.Dto;
using Furion.CMS.Core;
using Furion.CMS.Core.Base;
using Furion.CMS.Domain;
using Furion.DatabaseAccessor;
using Furion.DistributedIDGenerator;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Furion.FriendlyException;
using Furion.TaskScheduler;
using Furion.DependencyInjection;
using Furion.Logging.Extensions;
using Mapster;
using Furion.DynamicApiController;
using System.ComponentModel;

namespace Furion.CMS.Application.Services
{
    /// <summary>
    /// 定时任务应用服务
    /// </summary>
    [DisplayName("CrontabManager")]
    public class CrontabAppService : CurdAuditAppService<
        CrontabInfo,
        Guid,
        InputCrontabDto,
        InputCrontabDto,
        CrontabDto,
        CrontabDetailDto,
        CrontabFilter,
        CrontabSort>, IScoped
    {
        private readonly IDistributedIDGenerator _idGenerator;
        private readonly IRepository<CrontabExecuteRecord> _executeRecordRepository;

        public CrontabAppService(IRepository<CrontabInfo> repository,
            IDistributedIDGenerator idGenerator,
            IRepository<CrontabExecuteRecord> executeRecordRepository) : base(repository)
        {
            _idGenerator = idGenerator;
            _executeRecordRepository = executeRecordRepository;
        }

        /// <summary>
        /// 获取后台工作分页列表
        /// </summary>
        /// <param name="index"></param>
        /// <param name="limit"></param>
        /// <param name="filter"></param>
        /// <param name="sort"></param>
        /// <returns></returns>
        public override PagingList<CrontabDto> GetPagedList([ApiSeat(ApiSeats.ActionStart)] int index, [ApiSeat(ApiSeats.ActionStart)] int limit, [FromQuery] CrontabFilter filter, [FromBody] CrontabSort sort = null)
        {
            return base.GetPagedList(index, limit, filter, sort);
        }

        protected override List<CrontabDto> PagedlistFilterEndQueryHandle(IQueryable<CrontabInfo> entities)
        {
            return entities.Adapt<List<CrontabDto>>();
        }

        /// <summary>
        /// 获取后台工作详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public override CrontabDetailDto GetDetail(Guid id)
        {
            return base.GetDetail(id);
        }

        /// <summary>
        /// 创建任务
        /// </summary>
        /// <param name="dto"></param>
        public override void Create(InputCrontabDto dto)
        {
            base.Create(dto);
        }

        protected override void BeforeCreate(InputCrontabDto dto, CrontabInfo entity)
        {
            base.BeforeCreate(dto, entity);
            var flag = _appRepository.AsQueryable(false).Any(t => t.InstancePath == dto.InstancePath);
            if (flag) throw Oops.Oh("当前实例已存在,请勿重复添加");
            entity.Id = _idGenerator.Create().ToTryGuid();
        }

        /// <summary>
        /// 更新任务
        /// </summary>
        /// <param name="id"></param>
        /// <param name="dto"></param>
        public override void Update([ApiSeat(ApiSeats.ActionStart)] Guid id, [FromBody] InputCrontabDto dto)
        {
            base.Update(id, dto);
        }

        protected override CrontabInfo BeforeUpdate(InputCrontabDto input, CrontabInfo oldEntity, CrontabInfo newEntity, params string[] ignoreProperties)
        {
            var flag = _appRepository.AsQueryable(false).Any(t => t.InstancePath == input.InstancePath && t.Id != oldEntity.Id);
            if (flag) throw Oops.Oh("当前实例已存在,请勿重复添加");

            return base.BeforeUpdate(input, oldEntity, newEntity, ignoreProperties);
        }

        /// <summary>
        /// 删除任务
        /// </summary>
        /// <param name="ids"></param>
        public override void Delete([FromBody] IList<Guid> ids)
        {
            base.Delete(ids);
        }

        /// <summary>
        /// 检查实例是否可用
        /// </summary>
        /// <param name="path"></param>
        /// <param name="id"></param>
        [HttpGet]
        [QueryParameters]
        public void CheckInstance(string path, Guid? id)
        {
            var flag = _appRepository.AsQueryable(false)
                .Where(t => t.InstancePath == path)
                .Where(id.HasValue, t => t.Id != id)
                .Any();

            if (flag) throw Oops.Oh("当前实例已存在,请勿重复添加");
        }

        /// <summary>
        /// 获取后台工作定义列表
        /// </summary>
        /// <returns></returns>
        public List<EnumOptionDto> GetDefinitions([FromServices] IServiceProvider serviceProvider)
        {
            var jobDefinitions = serviceProvider.GetServices<ICrontabDefinition>();
            var jobList = jobDefinitions.Select(s => new EnumOptionDto { Label = s.DisplayName, Value = s.GetType().FullName }).ToList();

            return jobList;
        }

        /// <summary>
        /// 获取任务的执行记录
        /// </summary>
        /// <param name="index"></param>
        /// <param name="limit"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public PagingList<CrontabExecuteRecordDto> GetExecuteRecords([ApiSeat(ApiSeats.ActionStart)] int index, [ApiSeat(ApiSeats.ActionStart)] int limit,
            [FromQuery] Guid id)
        {
            var query = _executeRecordRepository.AsQueryable(false)
                .Where(t => t.TaskId == id)
                .OrderByDescending(o => o.CreatedTime);

            // 排序之后再进行分页处理
            var pageResult = new PagingList<CrontabExecuteRecordDto> { Current = index, PageSize = limit, Total = query.Count() };

            pageResult.List = query.AsPagedQuery(index, limit).Adapt<List<CrontabExecuteRecordDto>>();

            return pageResult;
        }

        /// <summary>
        /// 运行任务
        /// </summary>
        /// <param name="id"></param>
        [HttpGet]
        public void Start([ApiSeat(ApiSeats.ActionStart)] Guid id)
        {
            var entity = _appRepository.AsQueryable(false).FirstOrDefault(t => t.Id == id);

            if (entity == null)
                throw Oops.Bah($"未能找到ID为 {id} 的数据");

            if (!entity.Enable)
                throw Oops.Bah("启动失败,任务未启用");

            var worker = SpareTime.GetWorker(entity.InstancePath);
            if (worker != null)
                throw Oops.Bah("当前任务已激活,请勿重复激活");

            /*
             * 启动任务
             * 原Cron启动方式存在BUG,无法捕获异常
             */
            SpareTime.Do(entity.CronExpression, (timer, count) =>
            {
                Scoped.Create((_, scope) =>
                {
                    var services = scope.ServiceProvider;
                    // 获取数据库上下文
                    var dbContext = Db.GetDbContext(services);
                    var respository = Db.GetRepository<CrontabExecuteRecord>(services);

                    // 预插入执行任务记录
                    var record = new CrontabExecuteRecord
                    {
                        TaskId = timer.Description.ToTryGuid(),
                        ExecuteStatus = CrontabExecuteStatus.Run
                    };
                    respository.InsertNow(record);

                    // 获取当前所有的工作任务集合
                    var jobs = services.GetServices<ICrontabDefinition>();

                    try
                    {
                        // 找到任务实例
                        var jobInstance = jobs.FirstOrDefault(t => t.GetType().FullName == timer.WorkerName);
                        if (jobInstance == null) throw Oops.Bah("启动失败,未找到任务实例");

                        // 执行任务
                        jobInstance.Execute();
                        record.Result = jobInstance.GetResult();
                        record.ExecuteStatus = CrontabExecuteStatus.Success;
                    }
                    catch (Exception ex)
                    {
                        record.Result = $"错误:{ex.Message}";
                        record.ExecuteStatus = CrontabExecuteStatus.Fail;
                    }
                    finally
                    {
                        record.UpdatedTime = DateTimeOffset.UtcNow;
                        respository.UpdateNow(record);
                    }
                });
            }, entity.InstancePath, entity.Id.ToString(),
            cronFormat: CronFormat.IncludeSeconds,
            executeType: SpareTimeExecuteTypes.Serial);
        }

        /// <summary>
        /// 停止任务
        /// </summary>
        /// <param name="id"></param>
        [HttpGet]
        public void Stop([ApiSeat(ApiSeats.ActionStart)] Guid id)
        {
            var entity = _appRepository.AsQueryable(false).Where(t => t.Id == id)
                   .FirstOrDefault();

            if (entity == null) throw Oops.Bah($"未能找到ID为 {id} 的数据");

            SpareTime.Cancel(entity.InstancePath);
        }
    }
}
